#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"
#include "Type.h"
#include  <typeinfo>
#include <iostream>
#include <fstream>
using namespace std;
////////////////////////////////////////////////////////////////////////////////

void accFichier::ouvrirF() {
    fichier.open("transcod.c", ios_base::in | ios_base::out | ios_base::app);
    if (fichier) {
        string s;
        s = "int main (void){\n";
        size_t longueur = s.size();
        //fichier.write((char*) &longueur, sizeof (longueur));
        fichier.write(s.data(), longueur);

    } else {
        cerr << "Erreur à l'ouverture !" << endl;
        exit(0);
    }
}

void accFichier::fermerF() {
    string s;
    s = "\n}";
    size_t longueur = s.size();
    //fichier.write((char*) &longueur, sizeof (longueur));
    fichier.write(s.data(), longueur);
    fichier.close();
}

void accFichier::ecrireF(string code) {
    code = code + "\n";
    size_t longueur = code.size();
    //fichier.write((char*) &longueur, sizeof (longueur));
    fichier.write(code.data(), longueur);
}

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

Type * NoeudSeqInst::getValeur() {
    Type* valeur = NULL;
    for (unsigned int i = 0; i < tabInst.size(); i++)
        valeur = tabInst[i]->getValeur(); // on evalue chaque instruction de la séquence
    return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
    for (unsigned int i = 0; i < tabInst.size(); i++)
        tabInst[i]->afficher(indentation + 1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
    tabInst.push_back(instruction);
}
void NoeudSeqInst::transcod(accFichier aF){
        for (unsigned int i = 0; i < tabInst.size(); i++){
        tabInst[i]->transcod(&aF);}
    
}
////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
    this->variable = variable;
    this->expression = expression;
}

////////////////////////////////////////////////////////////////////////////////

Type * NoeudAffectation::getValeur() {
    Type* valeur = expression->getValeur(); // on évalue l'expression
    ((SymboleValue*) variable)->setValeur(valeur); // on affecte la variable
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////

void NoeudAffectation::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Affectation" << endl;
    variable->afficher(indentation + 1); // on affiche variable et expression
    expression->afficher(indentation + 1); // en augmentant l'indentation
}
////////////////////////////////////////////////////////////////////////////////

void NoeudAffectation::transcod(accFichier aF) {
    SymboleValue* var = dynamic_cast<SymboleValue*> (variable);
     aF.ecrireF(var->getChaine());
    aF.ecrireF("=");
    if(typeid(*expression) == typeid(SymboleValue) )
    {
       var = dynamic_cast<SymboleValue*> (expression);
       aF.ecrireF(var->getChaine());
    }
    else {
         expression->transcod(&aF);
    }
}


////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////

Type* NoeudOperateurBinaire::getValeur() {
    Type* valeur = NULL;
    Type* og = operandeGauche->getValeur();
    Type* od = operandeDroit->getValeur();
    //    Type* valOg;
    //    Type* valOd;

    //    if (typeid (*og) == typeid (TypeInt)) {// og
    //        valOg = dynamic_cast<TypeInt*> (og);
    //    } else if (typeid (*og) == typeid (TypeFloat)) {
    //        valOg = dynamic_cast<TypeFloat*> (og);
    //    } else if (typeid (*og) == typeid (TypeString)) {
    //        valOg = dynamic_cast<TypeString*> (og);
    //    } else {
    //        cout << "ta geule ca marche pas !!!" << endl;
    //        exit(0);
    //    }
    //
    //    if (typeid (*od) == typeid (TypeInt)) { //od
    //        valOd = dynamic_cast<TypeInt*> (od);
    //    } else if (typeid (*od) == typeid (TypeFloat)) {
    //        valOd = dynamic_cast<TypeFloat*> (od);
    //    } else if (typeid (*od) == typeid (TypeString)) {
    //        valOd = dynamic_cast<TypeString*> (od);
    //    } else {
    //        cout << "ta geule ca marche pas !!!" << endl;
    //        exit(0);
    //    }
    if (typeid (*od) == typeid (*og)) {
        if (typeid (*od) == typeid (TypeInt)) {
            int vg = (dynamic_cast<TypeInt*> (og))->getValeur();
            int vd = (dynamic_cast<TypeInt*> (od))->getValeur();

            if (this->operateur == "+") valeur = new TypeInt(vg + vd);
            else if (this->operateur == "-") valeur = new TypeInt(vg - vd);
            else if (this->operateur == "*") valeur = new TypeInt(vg * vd);
            else if (this->operateur == "et") if (vg && vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == "ou") if (vg || vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == "<") if (vg < vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == ">") if (vg > vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == "==") if (vg == vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == ">=") if (vg >= vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == "<=") if (vg <= vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == "!=") if (vg != vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else // this->operateur=="/"
            {
                if (vd != 0)
                    valeur = new TypeInt(vg / vd);
                else {
                    cout << "Erreur pendant l'interpretation : division par zero" << endl;
                    exit(0); // plus tard on levera une exception
                }
            }
        } else if (typeid (*od) == typeid (TypeFloat)) {
            float vg = (dynamic_cast<TypeFloat*> (og))->getValeur();
            float vd = (dynamic_cast<TypeFloat*> (od))->getValeur();

            if (this->operateur == "+") valeur = new TypeFloat(vg + vd);
            else if (this->operateur == "-") valeur = new TypeFloat(vg - vd);
            else if (this->operateur == "*") valeur = new TypeFloat(vg * vd);
            else if (this->operateur == "et") if (vg && vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == "ou") if (vg || vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == "<") if (vg < vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == ">") if (vg > vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == "==") if (vg == vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == ">=") if (vg >= vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == "<=") if (vg <= vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == "!=") if (vg != vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else // this->operateur=="/"
            {
                if (vd != 0.0)
                    valeur = new TypeFloat(vg / vd);

                else {
                    cout << "Erreur pendant l'interpretation : division par zero" << endl;
                    exit(0); // plus tard on levera une exception
                }
            }
        } else if (typeid (*od) == typeid (TypeString)) {
            string vg = (dynamic_cast<TypeString*> (og))->getValeur();
            string vd = (dynamic_cast<TypeString*> (od))->getValeur();

            if (this->operateur == "+") valeur = new TypeString(vg + vd);
            else if (this->operateur == "<") if (vg < vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == ">") if (vg > vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == "==") if (vg == vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == ">=") if (vg >= vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == "<=") if (vg <= vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else if (this->operateur == "!=") if (vg != vd) valeur = new TypeBool(true);
                else valeur = new TypeBool(false);
            else // this->operateur=="/"
            {
                cout << "operation entre deux chaines impossible !" << endl;
                exit(0); // plus tard on levera une exception
            }
        } else {
            cout << "heu... j'connaîs pas ce type !!!!!!!!" << endl;
        }
    } else {
        cout << "Erreur type différents " << endl;
        exit(0);
    }
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurBinaire::afficher(unsigned short indentation) {

    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}

void NoeudOperateurBinaire::transcod(accFichier aF) {
    operandeGauche->transcod(&aF);
    
    if(typeid(*operandeGauche) == typeid(SymboleValue) )
    {
        SymboleValue* var = dynamic_cast<SymboleValue*> (operandeGauche);
       aF.ecrireF(var->getChaine());
    }
    else
        operandeGauche->transcod(&aF);
    
    aF.ecrireF(this->operateur.getChaine());
    
    if(typeid(*operandeDroit) == typeid(SymboleValue) )
    {
        SymboleValue* var = dynamic_cast<SymboleValue*> (operandeDroit);
       aF.ecrireF(var->getChaine());
    }
    else
         operandeDroit->transcod(&aF);
}

NoeudSi::NoeudSi() {
}

void NoeudSi::afficher(unsigned short indentation) {

    Noeud::afficher(indentation);

    for (int i = 0; i < v_conditions.size(); i++) {
        v_conditions[i]->afficher(indentation + 1);
        v_instrus[i]->afficher(indentation + 1);
    }
    if (inst_sinon != NULL) { // Si on a une instruction sinon

        inst_sinon->afficher(indentation + 1);
    }
}

void NoeudSi::addI(Noeud * noeud) {

    v_instrus.push_back(noeud);
}

void NoeudSi::addC(Noeud * noeud) {

    v_conditions.push_back(noeud);
}

void NoeudSi::addS(Noeud * noeud) {

    this->inst_sinon = noeud;
}

void NoeudSi::transcod(accFichier aF) {

    for (int i = 0; i < v_conditions.size(); i++) {

        aF.ecrireF("if(");
        v_conditions[i]->transcod(&aF);
        aF.ecrireF("){\n");
        v_instrus[i]->transcod(&aF);
        aF.ecrireF("}else{\n");
    }
    if (inst_sinon != NULL) { // Si on a une instruction sinon

        inst_sinon->transcod(&aF);
        aF.ecrireF("}\n");
    }
}
////////////////////////////////////////////////////////////////////////////////

NoeudLire::NoeudLire(Symbole s) {

    this->symb = s;
}

void NoeudLire::afficher(unsigned short indentation) {

    Noeud::afficher(indentation);
    cout << "Noeud - Lire applique a : " << endl;
    SymboleValue sv(this->symb);
    sv.afficher(indentation + 1);
}

void NoeudLire::transcod(accFichier aF) {
    aF.ecrireF(symb.getChaine());
}
////////////////////////////////////////////////////////////////////////////////

NoeudBoucle::NoeudBoucle(Noeud * noeudC, Noeud * noeudS) {

    addC(noeudC);
    addS(noeudS);
}

void NoeudBoucle::addC(Noeud * noeud) {

    condition = noeud;
}

void NoeudBoucle::addS(Noeud * noeud) {

    seqinstr = noeud;
}

void NoeudBoucle::afficher(unsigned short indentation) {

    Noeud::afficher(indentation);
    cout << "Noeud - Tantque : " << endl;
    condition->afficher(indentation);
    Noeud::afficher(indentation);
    cout << "Noeud - Faire : " << endl;
    seqinstr->afficher(indentation + 1);
}

void NoeudBoucle::transcod(accFichier aF) {
    aF.ecrireF("while(");
    condition->transcod(&aF);
    aF.ecrireF("){\n");
    seqinstr->transcod(&aF);
    aF.ecrireF("}\n");
}
////////////////////////////////////////////////////////////////////////////////

void NoeudRepeterJsqa::afficher(unsigned short indentation) {

    Noeud::afficher(indentation);
    cout << "Noeud - Instruction repetée : " << endl;
    seqinstr2->afficher(indentation);
    Noeud::afficher(indentation);
    cout << "Noeud - Jusqu'à ce que : " << endl;
    condition->afficher(indentation + 1);
}

NoeudRepeterJsqa::NoeudRepeterJsqa(Noeud * condi, Noeud * seqinst, Noeud * seqinst2) : NoeudBoucle(condi, seqinst) {

    addS2(seqinst2);
}

void NoeudRepeterJsqa::addS2(Noeud * seqinst2) {

    seqinstr2 = seqinst2;
}

void NoeudRepeterJsqa::transcod(accFichier aF) {

    aF.ecrireF("do{\n");
    seqinstr->transcod(&aF);
    aF.ecrireF("}\n");
    aF.ecrireF("while(");
    condition->transcod(&aF);
    aF.ecrireF(");\n");
}
////////////////////////////////////////////////////////////////////////////////

NoeudPour::NoeudPour(Noeud * affect1, Noeud * expBool, Noeud * affect2, Noeud * seqinst) : NoeudBoucle(expBool, seqinst) {

    addA1(affect1);
    addA2(affect2);
}

void NoeudPour::afficher(unsigned short indentation) {

    Noeud::afficher(indentation);
    cout << "Noeud - Pour : " << endl;
    affectation1->afficher(indentation);
    Noeud::afficher(indentation + 1);
    cout << "Noeud - Jusqu'à ce que : " << endl;
    condition->afficher(indentation + 2);
    Noeud::afficher(indentation + 1);
    cout << "Noeud - Avec : " << endl;
    affectation2->afficher(indentation + 2);
    Noeud::afficher(indentation + 1);
    cout << "Noeud - Faire : " << endl;
    seqinstr->afficher(indentation + 2);

}

void NoeudPour::addA1(Noeud * affect1) {

    affectation1 = affect1;
}

void NoeudPour::addA2(Noeud * affect2) {

    affectation2 = affect2;
}

void NoeudPour::transcod(accFichier aF) {

    aF.ecrireF("for(");
    affectation1->transcod(&aF);
    aF.ecrireF(";");
    condition->transcod(&aF);
    aF.ecrireF(";");
    affectation2->transcod(&aF);
    aF.ecrireF("){\n");
    seqinstr->transcod(&aF);
    aF.ecrireF("}\n");
}
////////////////////////////////////////////////////////////////////////////////

NoeudEcrire::NoeudEcrire(Noeud* noeud) {

    this->argument = noeud;
}

void NoeudEcrire::afficher(unsigned short indentation) {

    Noeud::afficher(indentation);
    cout << "Noeud - Ecrire applique a : " << endl;
    argument->afficher(indentation + 1);
}

////////////////////////////////////////////////////////////////////////////////

NoeudChaine::NoeudChaine(Symbole s) {

    this->symb = s;
}

void NoeudChaine::afficher(unsigned short indentation) {

    Noeud::afficher(indentation);
    cout << "Noeud - Lire applique a : " << endl;
    SymboleValue sv(this->symb);
    sv.afficher(indentation + 1);
}

void NoeudChaine::transcod(accFichier aF) {
    aF.ecrireF(symb.getChaine());
}
